use super::ukey::Ukey;
use super::raw::Raw;
use super::mmap::{Mmap};
use super::head::head;
use super::gp::GP;

use std::io::{Error,ErrorKind,Result};
use std::fs::OpenOptions;
use std::path::Path;

#[derive(Debug)]
pub struct MemoryRegion {
	pub id : Ukey,
	base : Raw,
	pub size : usize,
    cp: usize,
	attached : bool,
    data_h: Mmap,
    cp_h: Mmap,
}

impl MemoryRegion {
    pub fn new(fname:String) -> Result<Option<MemoryRegion>> {
        let p = Path::new(&fname);
        let h = head::read_head(p)?;
        let id = h.check(fname.clone());
        match id {
            Some(key) => {
                let ds = h.data_size as usize;
                if ds == 0 {
                    return Ok(None);
                }
                let cs = h.cp_size as usize;
                let f = OpenOptions::new().read(true).write(true).open(p)?;
                let all = {
                    let tmp = Mmap::map_mut(ds+cs,&f,0 as u64)?;
                    tmp.usize()
                };

                let data_start = all as *mut libc::c_void;
                let data_h = Mmap::map_with_start(data_start,ds,&f,h.meta_size)?;
                let cp_start = (all+ds as usize) as *mut libc::c_void;
                let cp_h = Mmap::map_with_start(cp_start,cs,&f,h.meta_size)?;
                Ok(Some(MemoryRegion{
                    id:key,
                    base:data_h.to_raw(),
                    size:ds,
                    cp:cs,
                    attached:true,
                    data_h:data_h,
                    cp_h:cp_h,
                }))
            },
            None => Err(Error::new(ErrorKind::Other, "fail check"))
        }
    }
    pub fn valid_address(&self,p : &Raw) -> bool {
        let b = self.base.h.unwrap() as usize;
        let e = b + self.size;
        match p.h {
            Some(h) => {
                let hh = h as usize;
                if hh>b && hh<e {
                    true
                } else {
                    false
                }
            },
            None => false,
        }
	}
    pub fn valid_offset(&self,off : usize) -> bool {off<self.size}
    pub fn offset_to_raw(&self,offset : usize) -> Raw {
        if self.valid_offset(offset) {
            self.base.from_offset(offset)
        } else {
            Raw::default()
        }
    }
    pub fn raw_to_offset(&self,r:&Raw) -> Result<usize> {self.base.to_offset(r)}
    pub fn raw_to_gp(&self,r:&Raw) -> Result<GP> {
        let o = self.raw_to_offset(r)?;
        Ok(GP{id:self.id,offset:o as u64,size:0_u64})
    }
    pub fn valid_size(&self,size : usize) -> bool {size<self.cp}
    pub fn valid_gp(&self,g:&GP) -> bool {
        (self.id == g.id) 
        && self.valid_offset(g.offset as usize) 
        && self.valid_size(g.size as usize)
    }
    pub fn to_local(&self,g:&GP) -> GP {
        let mut r = *g;
        r.offset %= self.size as u64;
        r
    }
}